using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using NBusiness.CodeDom.Compiler;
using System.Text.RegularExpressions;
using NBusiness.CodeDom.Services;

namespace NBusiness.CodeDom
{
    [Serializable]
    public abstract class EntityCompileUnit : ICompileUnit
    {
        private EntityProvider _provider;

        private List<AssignmentLogic> _assignments = new List<AssignmentLogic>();
        private List<EntityElement> _elements = new List<EntityElement>();

        public EntityCompileUnit(EntityProvider provider)
            : this()
        {
            _provider = provider;
        }

        public EntityCompileUnit()
        {
            InitializeAssignmentLogic();
        }

        protected abstract void InitializeAssignmentLogic();

        protected virtual void Build(Stack<EntityElement> elements, EntityElementType lastSignificant)
        {
            if (elements == null)
                throw new ArgumentNullException("elements", "Invalid element");

            PostAssignmentAction action = PostAssignmentAction.Continue;
            while (elements.Count > 0 && action == PostAssignmentAction.Continue)
            {
                if (elements.Peek().Type == EntityElementType.Unknown)
                {
                    foreach (AssignmentLogic al in _assignments)
                    {
                        if (al.Assign(elements, lastSignificant))
                        {
                            action = al.PostAssignmentAction;
                            lastSignificant = (al.AssignmentType.IsSignificant && elements.Count > 0 ? 
                                elements.Peek().Type : 
                                lastSignificant);

                            break;
                        }
                    }
                }

                //index could progress in the assignment handler
                if (elements.Count > 0 &&
                    action == PostAssignmentAction.Continue)
                {
                    EntityElement e = elements.Pop();
                    Elements.Add(e);
                }
            }
        }
        
        protected abstract void Attach();

        protected virtual EntityElement GetElementFromChild(string path, int line, int col)
        {
            return null;
        }

        protected EntityElement GetElementByLocation(string path, int line, int col)
        {
            EntityElement found = null;
            var query = from
                              e in Elements
                          where
                              e.Path == path &&
                              e.Line == line &&
                              col >= e.Column &&
                              col <= e.Column + (e.EndOffset - e.BeginOffset)
                          select e;

            found = query.FirstOrDefault();
            if (found == null)
                found = GetElementFromChild(path, line, col);

            return found;
        }

        protected ICompileUnit GetUnitByElement(EntityElement element)
        {
            ICompileUnit unit = null;

            if (Elements.Contains(element)) 
                unit = this;
            else 
                unit = GetUnitFromChild(element);

            return unit;
        }

        protected virtual ICompileUnit GetUnitFromChild(EntityElement element)
        {
            return null;
        }

        protected IList<AssignmentLogic> Assignments
        {
            get { return _assignments; }
        }

        protected List<EntityElement> Elements { get { return _elements; } }

        protected EntityProvider Provider
        {
            get
            {
                EntityProvider provider = _provider;
                IHasParent hasParent = this as IHasParent;
                if (hasParent != null && hasParent.Parent != null)
                    provider = (hasParent.Parent as ICompileUnit).Provider;

                return provider;
            }
        }

        protected void OnBuildError(EntityBuildError buildError)
        {
            if (Provider != null)
            {
                IBuildErrorService buildErrorService = Provider.GetService(typeof(IBuildErrorService)) as IBuildErrorService;
                if (buildErrorService != null)
                    buildErrorService.Add(buildError);
            }
        }

        #region ICompileUnit Members
        
        EntityElement ICompileUnit.GetElementFromChild(string path, int line, int col)
        {
            return GetElementFromChild(path, line, col);
        }
        EntityElement ICompileUnit.GetElementByLocation(string path, int line, int col)
        {
            return GetElementByLocation(path, line, col);
        }
        ICompileUnit ICompileUnit.GetUnitByElement(EntityElement element)
        {
            return GetUnitByElement(element);
        }
        void ICompileUnit.Build(Stack<EntityElement> elements, EntityElementType lastSignificant)
        {
            Build(elements, lastSignificant);
        }
        void ICompileUnit.Attach()
        {
            Attach();
        }
        List<EntityElement> ICompileUnit.Elements
        {
            get { return Elements; }
        }
        EntityProvider ICompileUnit.Provider { get { return Provider; } }

        #endregion
    }

    public interface ICompileUnit
    {
        void Build(Stack<EntityElement> elements, EntityElementType lastSignificant);
        void Attach();

        EntityElement GetElementFromChild(string path, int line, int col);
        EntityElement GetElementByLocation(string path, int line, int col);
        ICompileUnit GetUnitByElement(EntityElement element);
        List<EntityElement> Elements { get; }
        EntityProvider Provider { get; }
    }
}
